﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Authentication;
using System.Web;
using System.Web.Security;
using CookComputing.XmlRpc;
using Microsoft.Practices.ServiceLocation;
using MvcCms.Data;
using MvcCms.Service;
using MvcCms.Service.Cache;
using MvcCms.Service.Code;

namespace MvcCms.Web
{
    [XmlRpcService(
        Name = "MvcCms MetaWeblog Windows Live Writer Service",
        Description = "Edit MvcCms with Windows Live Writer.",
        AutoDocumentation = true)]
    [XmlRpcUrl("http://www.mvccms.com/WLWMvcCms.ashx")] // Change this to your local host
    public class CmsEditor : XmlRpcService
    {
        private IAccountService accountService;
        private ICmsService cmsService;

        /// <summary>
        /// Authenticate user
        /// </summary>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        private static void Authenticate(string username, string password)
        {
            if (!DevCheck.IsDev())
            {
                if (!HttpContext.Current.Request.IsSecureConnection)
                {
                    throw new InvalidOperationException("HTTPS Required.  Access denied");
                }
            }

            //Check user credentials using form authentication
            bool allowed;

            if (!Membership.Provider.ValidateUser(username, password))
            {
                throw new InvalidCredentialException("Invalid credential.  Access denied");
            }

            if ((Roles.IsUserInRole(username, "Editor")) || (Roles.IsUserInRole(username, "Contributor")) ||
                (Roles.IsUserInRole(username, "Admin")))
            {
                allowed = true;
            }
            else
            {
                allowed = false;
            }
            if (allowed == false)
            {
                throw new InvalidCredentialException("Invalid credential.Access denied");
            }
        }

        /// <summary>
        /// Returns user's blogs
        /// </summary>
        /// <param name="appKey">Application Key</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <returns></returns>
        [XmlRpcMethod("blogger.getUsersBlogs")]
        public XmlRpcStruct[] getUsersBlogs(string appKey, string username, string password)
        {
            Authenticate(username, password);
            ICacheService cacheService = ServiceLocator.Current.GetInstance<ICacheService>();

            var currentPortal = cacheService.GetCurrentPortal();
            var rpcstruct = new XmlRpcStruct
                                {
                                    {"blogid", currentPortal.PortalID },
                                    {"blogName", currentPortal.Name + " Page Editor"},
                                    {"url", "http://" + currentPortal.URLAuthority + "/"}
                                };

            var datarpcstruct = new[] { rpcstruct };
            return datarpcstruct;
        }

        private static T[] ConvertListToArray<T>(List<T> myList)
        {
            if (myList == null)
                return null;

            var myArray = new T[myList.Count];

            for (int i = 0; i < myArray.Length; i++)
            {
                myArray[i] = myList[i];
            }
            return myArray;
        }


        /// <summary>
        /// Set blog post template
        /// </summary>
        /// <param name="appKey">Application Key</param>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="template">Template content</param>
        /// <param name="templateType">Template Type</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.setTemplate")]
        public bool setTemplate(string appKey, string blogid, string username, string password, string template,
                                string templateType)
        {
            Authenticate(username, password);
            /*
                TODO: Add implementation
            */
            return true;
        }

        /// <summary>
        /// Return list of blog category list
        /// </summary>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getCategories")]
        public XmlRpcStruct[] getCategories(string blogid, string username, string password)
        {
            Authenticate(username, password);
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();
            IEnumerable<ContentCategory> categories = cmsService.BuildCategoryListForBackOffice(cmsService.ListCategories());
            var contentAreas = new List<XmlRpcStruct>();
            foreach (ContentCategory cat in categories)
            {
                var rpcstruct = new XmlRpcStruct
                            {
                                {"categoryid", cat.CategoryID.ToString()},
                                {"title", cat.Path},
                                {"description", cat.Description.TryTrim()}
                            };
                contentAreas.Add(rpcstruct);
            }
            XmlRpcStruct[] aContA = ConvertListToArray(contentAreas);
            return aContA;
        }

        /// <summary>
        /// Returns recent blog posts
        /// </summary>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="userName">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="numberOfPosts">Number of posts to return</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getRecentPosts")]
        public XmlRpcStruct[] getRecentPosts(string blogid, string userName, string password, int numberOfPosts)
        {
            Authenticate(userName, password);
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();
            accountService = ServiceLocator.Current.GetInstance<IAccountService>();
            var cacheService = ServiceLocator.Current.GetInstance<ICacheService>();

            Portal currentPortal = cacheService.GetCurrentPortal();
            //Return a list of the content pages.  Return all if edtitor otherwise if contributor
            //return only pages assigned to that contributor.
            IEnumerable<DTOContentPageForEditor> pages = null;
            if (Roles.IsUserInRole(userName, "Editor"))
            {
                pages = cmsService.ListPages();
            }
            else
            {
                if (Roles.IsUserInRole(userName, "Contributor"))
                {
                    var user = accountService.GetUser(userName);
                    pages = cmsService.ListPages(user.UserID);
                }
            }
            var contentItems = new List<XmlRpcStruct>();
            if (pages != null)
                foreach (DTOContentPageForEditor item in pages)
                {
                    var rpcstruct = new XmlRpcStruct();

                    ContentCategory cat = cmsService.GetContentCategory(item.CategoryID);

                    rpcstruct.Add("description", item.Summary);
                    rpcstruct.Add("dateCreated", item.ReleaseDate);
                    rpcstruct.Add("title", item.Title);
                    rpcstruct.Add("link", "http://" + currentPortal.URLAuthority);
                    rpcstruct.Add("postid", item.ContentPageID.ToString());
                    rpcstruct.Add("categories", cat.Title);
                    contentItems.Add(rpcstruct);
                }

            XmlRpcStruct[] aContA = ConvertListToArray(contentItems);

            return aContA;
        }

        /// <summary>
        /// Return post template
        /// </summary>
        /// <param name="appKey">Application Key</param>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="templateType">Template Type</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getTemplate")]
        public string getTemplate(string appKey, string blogid, string username, string password, string templateType)
        {
            Authenticate(username, password);

            // TODO: add implementation with datasource
            const string template =
                @"<HTML>
                          <HEAD>
                            <TITLE><$BlogTitle$>: <$BlogDescription$></TITLE>
                          </HEAD>
                          <BODY >
                            <h1><$BlogTitle$></h1>

                              <!-- Blogger code begins here -->

                              <BLOGGER>
                               <BlogDateHeader>
                                   <b><h4><$BlogDateHeaderDate$>:</h4></b>
                              </BlogDateHeader>

                              <a name='<$BlogItemNumber$>'><$BlogItemBody$></a>
                              <br>
                              <small><$BlogItemAuthor$>
                              <br>
                              <center>______________________</center>
                              <br>
                              </p>
                              </BLOGGER>
                           '
                          </BODY>
                        </HTML>";
            return template;
        }


        /// <summary>
        /// Create new Post
        /// </summary>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="rpcstruct">Blog post XML-RPC structure</param>
        /// <param name="publish">TRUE to publish post after creation</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.newPost")]
        public string newPost(string blogid, string username, string password, XmlRpcStruct rpcstruct, bool publish)
        {
            //limit the catetories to the portal the user belongs to
            Authenticate(username, password);
            var acctService = ServiceLocator.Current.GetInstance<IAccountService>();

            var user = acctService.GetUser(username);
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();

            IEnumerable<ContentCategory> cats = cmsService.ListCategories().Where(cat => cat.Portal.PortalID.Equals(user.Portal.PortalID));
            var assignedCategoryId = 0;
            try
            {
                var arr = (string[])rpcstruct["categories"];
                foreach (ContentCategory cat in cats)
                {
                    if (cat.Path != arr[0]) continue;
                    assignedCategoryId = cat.CategoryID;
                    break;
                }
            }
            catch (Exception)
            {
                assignedCategoryId = cats.Where(c => c.Title.Equals("Home")).FirstOrDefault().CategoryID;
            }
            ContentCategory newPageCategory = cmsService.GetContentCategory(assignedCategoryId);
            var pageToCreate = new ContentPage
            {
                AddedDate = DateTime.Now,
                Editor = user,
                Contributor = user,
                Title = rpcstruct["title"].ToString(),
                Path = rpcstruct["title"].ToString().ToUrlFormat(),
                Summary = newPageCategory.Title + " Subpage " + rpcstruct["title"],
                Description = rpcstruct["title"].ToString(),
                Body = rpcstruct["description"].ToString(),
                ReleaseDate = DateTime.Today,
                ExpireDate = DateTime.Parse("12/12/2012"),
                Approved = true,
                Listed = true,
                UpdateDate = DateTime.Now,
                CommentsEnabled = true,
                OnlyForMembers = false,
                ViewCount = 0,
                Votes = 0,
                TotalRating = 0,
                IsCategoryPage = false,
                IsSubPage = true,
                AnonCommentsEnabled = false
            };

            pageToCreate.ContentCategory = newPageCategory;
            var newPageId = cmsService.CreateContentPage(pageToCreate).ToString();
            if (newPageId == "0")
            {
                var modelState = cmsService.GetModelStateDictionary();
                string message = modelState["Title"].Errors[0].ErrorMessage;
                if (message == "")
                {
                    message = "An error occured, please contact your web administrator.";
                }
                throw new ArgumentException(message);
            }
            return newPageId;
        }

        /// <summary>
        /// Edit existing Post
        /// </summary>
        /// <param name="postid">Post Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="rpcstruct">Blog post XML-RPC structure</param>
        /// <param name="publish">TRUE to publish post after modification</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.editPost")]
        public bool editPost(string postid, string username, string password, XmlRpcStruct rpcstruct, bool publish)
        {
            Authenticate(username, password);
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();
            int iPostId = int.Parse(postid);
            ContentPage article = cmsService.GetContentPage(iPostId);

            var newTitle = rpcstruct["title"].ToString();
            article.Body = rpcstruct["description"].ToString();
            article.UpdateDate = DateTime.Now;

            if (cmsService.EditContentPage(article, newTitle))
            {
                return true;
            }
            else
            {
                var modelState = cmsService.GetModelStateDictionary();
                string message = modelState["Title"].Errors[0].ErrorMessage;
                if (message == "")
                {
                    message = "An error occured, please contact your web administrator.";
                }
                throw new ArgumentException(message);
            }
        }


        /// <summary>
        /// Return existing post
        /// </summary>
        /// <param name="postid">Post Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <returns></returns>
        [XmlRpcMethod("metaWeblog.getPost")]
        public XmlRpcStruct getPost(string postid, string username, string password)
        {
            Authenticate(username, password);
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();
            int iPostid = int.Parse(postid);
            var cacheService = ServiceLocator.Current.GetInstance<ICacheService>();
            Portal currentPortal = cacheService.GetCurrentPortal();
            ContentPage article = cmsService.GetContentPage(iPostid);

            ContentCategory cat = cmsService.GetContentCategory(article.ContentCategory.CategoryID);
            var rpcstruct = new XmlRpcStruct
                                {
                                    {"description", article.Body},
                                    {"dateCreated", article.ReleaseDate},
                                    {"title", article.Title},
                                    {"link", "http://"+currentPortal.URLAuthority },
                                    {"postid", article.ContentPageID},
                                    {"categories", new string[] {cat.Path}}
                                };

            return rpcstruct;
        }

        /// <summary>
        /// Delete existing post
        /// </summary>
        /// <param name="appKey">Application key</param>
        /// <param name="postid">Post Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="publish"></param>
        /// <returns></returns>
        [XmlRpcMethod("blogger.deletePost")]
        public bool deletePost(string appKey, string postid, string username, string password, bool publish)
        {
            cmsService = ServiceLocator.Current.GetInstance<ICmsService>();
            var pageToDelete = cmsService.GetContentPage(int.Parse(postid));
            return cmsService.DeleteContentPage(pageToDelete);
        }

        /// <summary>
        /// Create new media object associated with post
        /// </summary>
        /// <param name="blogid">Blog Identificator</param>
        /// <param name="username">UserName</param>
        /// <param name="password">Password</param>
        /// <param name="rpcstruct">XML-RPC struct representing media object</param>
        /// <returns>struct with url location of object</returns>
        [XmlRpcMethod("metaWeblog.newMediaObject")]
        public XmlRpcStruct newMediaObject(string blogid, string username, string password, XmlRpcStruct rpcstruct)
        {
            Authenticate(username, password);
            var cacheService = ServiceLocator.Current.GetInstance<ICacheService>();
            Portal currentPortal = cacheService.GetCurrentPortal();
            string name = rpcstruct["name"].ToString(); //object name
            //string type = rpcstruct["type"].ToString(); //object type
            var media = (byte[])rpcstruct["bits"]; //object body

            int index = name.LastIndexOf('/');
            var subFolderName = name.Substring(0, index);

            index = subFolderName.LastIndexOf('/');
            subFolderName = subFolderName.Substring(index, subFolderName.Length - index - 1);

            string folderName = cacheService.GetCurrentPortal().Name.ToUrlFormat() + "/wlw";
            folderName += subFolderName;
            string path = HttpContext.Current.Server.MapPath("~/Uploadedimages/" + folderName);
            string fileName = Path.GetFileName(name);
            string url = "/Uploadedimages/" + folderName + "/" + fileName;

            string filePath = Path.Combine(path, fileName);

            Directory.CreateDirectory(path);
            FileStream stream = File.Create(filePath);
            stream.Write(media, 0, media.Length);
            stream.Flush();
            stream.Close();
            stream.Dispose();
            var rstruct = new XmlRpcStruct { { "url", url } };
            return rstruct;
        }
    }
}